﻿using System;
using System.Collections;
using System.Collections.Generic;
using Pathfinding;
using UnityEngine;
using Yoozoo.Gameplay.Liberty.StreetEvent;

#if UNITY_EDITOR
using EditorExtension;
using UnityEditor;
#endif

namespace Yoozoo.Gameplay.Liberty.Tools
{
    public class CollectCollidersToMeshTool : MonoBehaviour
    {
        public class CCMeshData
        {
            public Mesh sharedMesh;
            public Transform transform;
        }

        public class MeshNavMeshCutData
        {
            public Mesh originMesh;
            public Mesh navMeshCutMesh;
        }
        
        private Transform _ccRoot;
        private Transform _combineRoot;

        public Transform targetRoot;
        public List<MeshNavMeshCutData> meshCutDataList;
        public float minNavCutHeight = 1;
        public List<string> navLayers = new List<string>()
        {
            "sidewalk"
        };

        public string meshName = "ccTest";

        [Header("是否检测subMesh")]
        public bool subMeshCheck = true;
        
        [Space(20)] 
        [Header("这些点下的所有子物体会被检测")]
        public List<Transform> navPointParents;
        [Header("单个寻路可达点")]
        public List<Transform> navPoints;
        [Header("寻路检测起始点")]
        public Transform navCheckStartPoint;
        [Header("需要检测的寻路层")]
        public string checkNavLayerName = "sidewalk";
        
        private Dictionary<Mesh, MeshNavMeshCutData> _meshCutDataMap;

        public void Collect()
        {
#if UNITY_EDITOR

            if (targetRoot == null)
            {
                return;
            }

            if (subMeshCheck)
            {
                bool pass = true;
                foreach (var meshCollider in targetRoot.GetComponentsInChildren<MeshCollider>(true))
                {
                    if (meshCollider.sharedMesh == null)
                    {
                        Debug.LogError($"{meshCollider.transform.GetHierarchyPath()} meshCollider.sharedMesh = null.");
                        continue;
                    }
                    if (meshCollider.sharedMesh.subMeshCount > 1)
                    {
                        UnityEngine.Debug.LogError("MeshCollider 的 Mesh 不能有 SubMesh :" + meshCollider.sharedMesh.name,meshCollider.transform.gameObject);
                        pass = false;
                    }
                }
            
                if (!pass)
                {
                    EditorUtility.DisplayDialog("Error", "有MeshCollider的Mesh不符合规范，详细看Log", "ok");
                    return;
                }
            }

            _meshCutDataMap = new Dictionary<Mesh, MeshNavMeshCutData>();
            if (meshCutDataList!=null)
            {
                foreach (var meshCutData in meshCutDataList)
                {
                    _meshCutDataMap[meshCutData.originMesh] = meshCutData;
                }
            }

            var ocr = GameObject.Find("combineRoot");
            if (ocr)
            {
                GameObject.DestroyImmediate(ocr);
            }
            
            _ccRoot = new GameObject("ccRoot").transform;
            _combineRoot = new GameObject("combineRoot").transform;
            _combineRoot.gameObject.layer = LayerMask.NameToLayer("cityground");
            _ccRoot.position = Vector3.zero;
            _ccRoot.rotation = Quaternion.identity;
            _combineRoot.position = Vector3.zero;
            _combineRoot.rotation = Quaternion.identity;
            
            // try
            // {
                List<CCMeshData> cMeshDataList = new List<CCMeshData>(2000);

                var boxColliders = targetRoot.GetComponentsInChildren<BoxCollider>(true);
                foreach (var boxCollider in boxColliders)
                {
                    GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
                    cube.transform.parent = _combineRoot;
                    cube.layer = LayerMask.NameToLayer("cityground");

                    cube.transform.localEulerAngles = boxCollider.transform.eulerAngles;
                    cube.transform.localPosition = boxCollider.transform.position + boxCollider.transform.TransformVector(boxCollider.center);
                    Vector3 s = boxCollider.transform.lossyScale;
                    cube.transform.localScale = new Vector3(
                        s.x * boxCollider.size.x,
                        s.y * boxCollider.size.y,
                        s.z * boxCollider.size.z
                    );
                    
                    cMeshDataList.Add(new CCMeshData()
                    {
                        sharedMesh = cube.GetComponent<MeshFilter>().sharedMesh,
                        transform =  cube.transform
                    });

                    if (s.y * boxCollider.size.y > minNavCutHeight)  // 高度小于一定值不进行剔除处理
                    {
                        NavmeshCut navmeshCut = cube.AddComponent<NavmeshCut>();
                        navmeshCut.type = NavmeshCut.MeshType.Rectangle;
                        navmeshCut.useRotationAndScale = true;
                        navmeshCut.height = s.y * boxCollider.size.y;
                        navmeshCut.maskLayers = navLayers;
                    }
                }

                var meshColliders = targetRoot.GetComponentsInChildren<MeshCollider>(true);
                foreach (var meshCollider in meshColliders)
                {
                    cMeshDataList.Add(new CCMeshData()
                    {
                        sharedMesh = meshCollider.sharedMesh,
                        transform =  meshCollider.transform,
                    });

                    if (_meshCutDataMap.TryGetValue(meshCollider.sharedMesh,out var cutData))
                    {
                        GameObject plane = GameObject.CreatePrimitive(PrimitiveType.Plane);
                        plane.transform.parent = _combineRoot;
                        plane.layer = LayerMask.NameToLayer("cityground");
                        var meshFilter = plane.GetComponent<MeshFilter>();
                        meshFilter.sharedMesh = cutData.navMeshCutMesh;
                        plane.transform.localPosition = meshCollider.transform.position;
                        plane.transform.localEulerAngles = meshCollider.transform.eulerAngles;
                        plane.transform.localScale = meshCollider.transform.lossyScale;

                        NavmeshCut navmeshCut = plane.AddComponent<NavmeshCut>();
                        navmeshCut.type = NavmeshCut.MeshType.CustomMesh;
                        navmeshCut.mesh = cutData.navMeshCutMesh;
                        navmeshCut.useRotationAndScale = true;
                        navmeshCut.maskLayers = navLayers;
                    }
                }

                CombineInstance[] combine = new CombineInstance[cMeshDataList.Count];

                int i = 0;
                while (i < cMeshDataList.Count)
                {
                    combine[i].mesh = cMeshDataList[i].sharedMesh;
                    combine[i].transform = cMeshDataList[i].transform.localToWorldMatrix;
                    i++;
                }
                
                Mesh mesh = new Mesh();
                mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
                mesh.CombineMeshes(combine);
                
                SaveMesh(mesh, meshName, false);

                if (_combineRoot.gameObject.GetComponent<MeshFilter>() == null)
                {
                    _combineRoot.gameObject.AddComponent<MeshFilter>().sharedMesh = mesh;
                    _combineRoot.gameObject.AddComponent<MeshRenderer>();
                }
                else
                {
                    _combineRoot.gameObject.GetComponent<MeshFilter>().sharedMesh = mesh;
                }
                
            // }
            // catch (Exception e)
            // {
            //     Console.WriteLine(e);
            //     throw;
            // }
            // finally
            // {
            GameObject.DestroyImmediate(_ccRoot.gameObject);
            // }
#endif
        }
        
        public void SaveMesh (Mesh mesh, string name, bool optimizeMesh) {
#if UNITY_EDITOR
            string path = EditorUtility.SaveFilePanel("Save Separate Mesh Asset", "Assets/", name, "asset");
            if (string.IsNullOrEmpty(path)) return;
        
            path = FileUtil.GetProjectRelativePath(path);
            
            if (optimizeMesh)
                MeshUtility.Optimize(mesh);
        
            AssetDatabase.CreateAsset(mesh, path);
            AssetDatabase.SaveAssets();
#endif
        }

        public void CheckNavPoints()
        {
            var gs = AstarPath.active.graphs;
            RecastGraph recastGraph = null;
            foreach (var graph in gs)
            {
                if (graph.name == checkNavLayerName)
                {
                    recastGraph = graph as RecastGraph;
                }
            }

            List<Transform> allPoints = new List<Transform>(200);

            foreach (var pointParent in navPointParents)
            {
                for (int i = 0; i < pointParent.childCount; i++)
                {
                    allPoints.Add(pointParent.GetChild(i));
                }
            }

            var streetEvents = GetComponentsInChildren<StreetEventBase>(true);
            foreach (var streetEvent in streetEvents)
            {
                var pointParent = streetEvent.eventConfig.pointRoot;
                for (int i = 0; i < pointParent.childCount; i++)
                {
                    allPoints.Add(pointParent.GetChild(i));
                }
            }
            
            allPoints.AddRange(navPoints);

            StartCoroutine(CheckNavPath(allPoints, recastGraph));
        }

        IEnumerator CheckNavPath(List<Transform> allPoints,RecastGraph recastGraph)
        {
            yield return null;
#if UNITY_EDITOR
            foreach (var point in allPoints)
            {
                var graphNode = recastGraph.PointOnNavmesh(point.position, NNConstraint.Default);
                if (graphNode == null)
                {
                    UnityEngine.Debug.LogError("[寻路点检测] 点不在寻路网格上：" + point.name, point.gameObject);
                    continue;
                }

                bool success = false;
                var p = ABPath.Construct(navCheckStartPoint.position, point.position, (path) =>
                {
                    success = true;

                    if (path.IsDone() && path.vectorPath!=null && path.vectorPath.Count > 0)
                    {
                        var pathPoint = path.vectorPath[path.vectorPath.Count - 1];
                        // if (Vector3.Distance(pathPoint,point.position) > 0.5f)
                        // {
                        //     UnityEngine.Debug.LogError("[寻路点检测] 路径无法到达" + point.name, point.gameObject);
                        // }
                        //
                        var endNode = recastGraph.PointOnNavmesh(pathPoint, NNConstraint.Default);
                        if (endNode != graphNode)
                        {
                            UnityEngine.Debug.LogError("[寻路点检测] 路径无法到达" + point.name, point.gameObject);
                        }
                        
                        // foreach (var v in path.vectorPath)
                        // {
                        //     Debug.DrawLine(v,v+new Vector3(0,2,0),Color.green,100);
                        // }
                    }
                    else
                    {
                        UnityEngine.Debug.LogError("[寻路点检测] 没有找到路径：" + point.name, point.gameObject);
                    }
                });
                AstarPath.StartPath(p);
                yield return new WaitUntil(() => success);
            }

            EditorUtility.DisplayDialog("完成", "寻路点检测完成", "OK");
#endif
            
        }
    }
}